﻿Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Collections.Specialized
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing.Design
Imports System.Reflection
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Compiler
Imports System.Workflow.ComponentModel.Design
Imports System.Workflow.Runtime

Namespace System.Workflow.Activities
    <SRDescription("CallExternalMethodActivityDescription"), ActivityValidator(GetType(CallExternalMethodActivityValidator)), SRCategory("Base"), DefaultEvent("MethodInvoking"), Designer(GetType(CallExternalMethodActivityDesigner), GetType(IDesigner))> _
    Public Class CallExternalMethodActivity
        Inherits Activity
        Implements IPropertyValueProvider, IDynamicPropertyTypeProvider
        ' Events
        <SRDescription("OnBeforeMethodInvokeDescr"), SRCategory("Handlers"), MergableProperty(False)> _
        Public Custom Event MethodInvoking As EventHandler
            AddHandler(ByVal value As EventHandler)
                MyBase.AddHandler(CallExternalMethodActivity.MethodInvokingEvent, value)
            End AddHandler
            RemoveHandler(ByVal value As EventHandler)
                MyBase.RemoveHandler(CallExternalMethodActivity.MethodInvokingEvent, value)
            End RemoveHandler
        End Event

        ' Methods
        Public Sub New()
            MyBase.SetReadOnlyPropertyValue(CallExternalMethodActivity.ParameterBindingsProperty, New WorkflowParameterBindingCollection(Me))
        End Sub

        Public Sub New(ByVal name As String)
            MyBase.New(name)
            MyBase.SetReadOnlyPropertyValue(CallExternalMethodActivity.ParameterBindingsProperty, New WorkflowParameterBindingCollection(Me))
        End Sub

        Protected NotOverridable Overrides Function Execute(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (Me.InterfaceType Is Nothing) Then
                Throw New ArgumentException(SR.GetString("Error_MissingInterfaceType"), "executionContext")
            End If
            Dim interfaceType As Type = Me.InterfaceType
            Dim methodName As String = Me.MethodName
            Dim service As Object = executionContext.GetService(interfaceType)
            If (service Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_ServiceNotFound", New Object() { Me.InterfaceType }))
            End If
            MyBase.RaiseEvent(CallExternalMethodActivity.MethodInvokingEvent, Me, EventArgs.Empty)
            Me.OnMethodInvoking(EventArgs.Empty)
            Dim method As MethodInfo = interfaceType.GetMethod(methodName, (BindingFlags.Public Or BindingFlags.Instance))
            Dim parameterModifiers As ParameterModifier() = Nothing
            Dim messageArgs As Object() = InvokeHelper.GetParameters(method, Me.ParameterBindings, parameterModifiers)
            Dim binding As WorkflowParameterBinding = Nothing
            If Me.ParameterBindings.Contains("(ReturnValue)") Then
                binding = Me.ParameterBindings.Item("(ReturnValue)")
            End If
            CorrelationService.InvalidateCorrelationToken(Me, interfaceType, methodName, messageArgs)
            Dim source As Object = interfaceType.InvokeMember(Me.MethodName, BindingFlags.InvokeMethod, New ExternalDataExchangeBinder, service, messageArgs, parameterModifiers, Nothing, Nothing)
            If (Not binding Is Nothing) Then
                binding.Value = InvokeHelper.CloneOutboundValue(source, New BinaryFormatter, "(ReturnValue)")
            End If
            InvokeHelper.SaveOutRefParameters(messageArgs, method, Me.ParameterBindings)
            Me.OnMethodInvoked(EventArgs.Empty)
            Return ActivityExecutionStatus.Closed
        End Function

        Friend Sub GetParameterPropertyDescriptors(ByVal properties As IDictionary)
            If (Not Me.Site Is Nothing) Then
                If (DirectCast(Me.Site.GetService(GetType(ITypeProvider)), ITypeProvider) Is Nothing) Then
                    Throw New InvalidOperationException(SR.GetString("General_MissingService", New Object() { GetType(ITypeProvider).FullName }))
                End If
                If (MyBase.GetType Is GetType(CallExternalMethodActivity)) Then
                    Dim interfaceType As Type = Me.InterfaceType
                    If (Not interfaceType Is Nothing) Then
                        Dim method As MethodInfo = interfaceType.GetMethod(Me.MethodName)
                        If (Not method Is Nothing) Then
                            Dim list As New ArrayList(method.GetParameters)
                            If (Not method.ReturnType Is GetType(Void)) Then
                                list.Add(method.ReturnParameter)
                            End If
                            Dim info2 As ParameterInfo
                            For Each info2 In list
                                If (Not info2.ParameterType Is Nothing) Then
                                    Dim descriptor As PropertyDescriptor = New ParameterInfoBasedPropertyDescriptor(GetType(CallExternalMethodActivity), info2, True, New Attribute() { DesignOnlyAttribute.Yes })
                                    properties.Item(descriptor.Name) = descriptor
                                End If
                            Next
                        End If
                    End If
                End If
            End If
        End Sub

        Protected Overrides Sub InitializeProperties()
            ActivityHelpers.InitializeCorrelationTokenCollection(Me, Me.CorrelationToken)
            Dim interfaceType As Type = Me.InterfaceType
            If (interfaceType Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("InterfaceTypeMissing", New Object() { MyBase.Name }))
            End If
            Dim methodName As String = Me.MethodName
            If (methodName Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("MethodNameMissing", New Object() { MyBase.Name }))
            End If
            Dim method As MethodInfo = interfaceType.GetMethod(methodName)
            If (method Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("MethodInfoMissing", New Object() { Me.MethodName, Me.InterfaceType.Name }))
            End If
            InvokeHelper.InitializeParameters(method, Me.ParameterBindings)
            MyBase.InitializeProperties
        End Sub

        Protected Overridable Sub OnMethodInvoked(ByVal e As EventArgs)
        End Sub

        Protected Overridable Sub OnMethodInvoking(ByVal e As EventArgs)
        End Sub

        Private Function System.Workflow.ComponentModel.Design.IPropertyValueProvider.GetPropertyValues(ByVal context As ITypeDescriptorContext) As ICollection Implements IPropertyValueProvider.GetPropertyValues
            Dim strings As New StringCollection
            If (Not Me.InterfaceType Is Nothing) Then
                If Not (context.PropertyDescriptor.Name = "MethodName") Then
                    Return strings
                End If
                Dim info As MethodInfo
                For Each info In Me.InterfaceType.GetMethods((BindingFlags.Public Or (BindingFlags.Instance Or BindingFlags.DeclaredOnly)))
                    If Not info.IsSpecialName Then
                        strings.Add(info.Name)
                    End If
                Next
            End If
            Return strings
        End Function

        Private Function System.Workflow.ComponentModel.IDynamicPropertyTypeProvider.GetAccessType(ByVal serviceProvider As IServiceProvider, ByVal propertyName As String) As AccessTypes Implements IDynamicPropertyTypeProvider.GetAccessType
            If (propertyName Is Nothing) Then
                Throw New ArgumentNullException("propertyName")
            End If
            Return AccessTypes.Read
        End Function

        Private Function System.Workflow.ComponentModel.IDynamicPropertyTypeProvider.GetPropertyType(ByVal serviceProvider As IServiceProvider, ByVal propertyName As String) As Type Implements IDynamicPropertyTypeProvider.GetPropertyType
            If (propertyName Is Nothing) Then
                Throw New ArgumentNullException("propertyName")
            End If
            Dim properties As New Dictionary([Of] String, Object)
            Me.GetParameterPropertyDescriptors(properties)
            If properties.ContainsKey(propertyName) Then
                Dim descriptor As ParameterInfoBasedPropertyDescriptor = [TryCast](properties.Item(propertyName),ParameterInfoBasedPropertyDescriptor)
                If (Not descriptor Is Nothing) Then
                    Return descriptor.ParameterType
                End If
            End If
            Return Nothing
        End Function


        ' Properties
        <RefreshProperties(RefreshProperties.All), DefaultValue(CStr(Nothing)), TypeConverter(GetType(CorrelationTokenTypeConverter)), SRCategory("Activity"), SRDescription("CorrelationSetDescr"), MergableProperty(False)> _
        Public Overridable Property CorrelationToken As CorrelationToken
            Get
                Return [TryCast](MyBase.GetValue(CallExternalMethodActivity.CorrelationTokenProperty),CorrelationToken)
            End Get
            Set(ByVal value As CorrelationToken)
                MyBase.SetValue(CallExternalMethodActivity.CorrelationTokenProperty, value)
            End Set
        End Property

        <SRCategory("Activity"), DefaultValue(CStr(Nothing)), SRDescription("HelperExternalDataExchangeDesc"), RefreshProperties(RefreshProperties.All), Editor(GetType(TypeBrowserEditor), GetType(UITypeEditor)), TypeFilterProvider(GetType(ExternalDataExchangeInterfaceTypeFilterProvider))> _
        Public Overridable Property InterfaceType As Type
            Get
                Return [TryCast](MyBase.GetValue(CallExternalMethodActivity.InterfaceTypeProperty),Type)
            End Get
            Set(ByVal value As Type)
                MyBase.SetValue(CallExternalMethodActivity.InterfaceTypeProperty, value)
            End Set
        End Property

        <TypeConverter(GetType(PropertyValueProviderTypeConverter)), SRDescription("ExternalMethodNameDescr"), DefaultValue(""), SRCategory("Activity"), RefreshProperties(RefreshProperties.All), MergableProperty(False)> _
        Public Overridable Property MethodName As String
            Get
                Return [TryCast](MyBase.GetValue(CallExternalMethodActivity.MethodNameProperty),String)
            End Get
            Set(ByVal value As String)
                MyBase.SetValue(CallExternalMethodActivity.MethodNameProperty, value)
            End Set
        End Property

        <Browsable(False), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
        Public ReadOnly Property ParameterBindings As WorkflowParameterBindingCollection
            Get
                Return [TryCast](MyBase.GetValue(CallExternalMethodActivity.ParameterBindingsProperty),WorkflowParameterBindingCollection)
            End Get
        End Property


        ' Fields
        Public Shared ReadOnly CorrelationTokenProperty As DependencyProperty = DependencyProperty.Register("CorrelationToken", GetType(CorrelationToken), GetType(CallExternalMethodActivity), New PropertyMetadata(DependencyPropertyOptions.Metadata))
        Public Shared ReadOnly InterfaceTypeProperty As DependencyProperty = DependencyProperty.Register("InterfaceType", GetType(Type), GetType(CallExternalMethodActivity), New PropertyMetadata(Nothing, DependencyPropertyOptions.Metadata, New Attribute() { New ValidationOptionAttribute(ValidationOption.Required) }))
        Public Shared ReadOnly MethodInvokingEvent As DependencyProperty = DependencyProperty.Register("MethodInvoking", GetType(EventHandler), GetType(CallExternalMethodActivity))
        Public Shared ReadOnly MethodNameProperty As DependencyProperty = DependencyProperty.Register("MethodName", GetType(String), GetType(CallExternalMethodActivity), New PropertyMetadata("", DependencyPropertyOptions.Metadata, New Attribute() { New ValidationOptionAttribute(ValidationOption.Required) }))
        Public Shared ReadOnly ParameterBindingsProperty As DependencyProperty = DependencyProperty.Register("ParameterBindings", GetType(WorkflowParameterBindingCollection), GetType(CallExternalMethodActivity), New PropertyMetadata((DependencyPropertyOptions.Metadata Or DependencyPropertyOptions.ReadOnly), New Attribute() { New BrowsableAttribute(False), New DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content) }))
        Friend Shared ReadOnly ReservedParameterNames As ArrayList = New ArrayList(New String() { "Name", "Enabled", "Description", "MethodName", "MethodInvoking", "InterfaceType" })
    End Class
End Namespace

